﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using TableParsers;
using System.Collections.Observable;
using System.Collections.Specialized;

namespace SpeakupBackend.Themes
{
    [Serializable]
    [XmlRoot("BusinessThemeCollection")]
    public class BusinessThemeCollection : ObservableCollectionBase<BusinessTheme, HashSet<BusinessTheme>>, IXmlSerializable
    {
        public BusinessThemeCollection()
            : base(new HashSet<BusinessTheme>())
        {
        }

        /// <summary>
        /// Can be used only for just loaded collections.
        /// I.e. no event subscribers are present.
        /// </summary>
        /// <param name="other"></param>
        /// <param name="useTheirsOnConflict"></param>
        void Merge(BusinessThemeCollection other, bool useTheirsOnConflict)
        {
            using (var transaction = BeginTransaction())
            {
                foreach (var theirsItem in other.InternalCollection)
                {
                    if (InternalCollection.Add(theirsItem))
                    {
                        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, theirsItem));
                    }
                    else if (useTheirsOnConflict)
                    {
                        var conflictingTheme = (from th in InternalCollection
                                                where th == theirsItem
                                                select th).First();


                        conflictingTheme.Name = theirsItem.Name;
                    }
                }
            }
        }

        #region IXmlSerializable implementation

        private static readonly string ThemeElementName = "BusinessTheme";

        public XmlSchema GetSchema()
        {
            throw new NotImplementedException();
        }

        public void ReadXml(XmlReader reader)
        {
            reader.ReadStartElement();

            while (reader.IsStartElement())
            {
                if (!reader.IsStartElement(ThemeElementName))
                {
                    throw new XmlException(String.Format("Node name {0} is not recognized.", reader.LocalName));
                }

                BusinessTheme theme = new BusinessTheme();
                theme.ReadXml(reader);

                if (Contains(theme))
                {
                    Remove(theme);
                }
                Add(theme);
            }
        }

        public void WriteXml(XmlWriter writer)
        {
            var sortedList = from n in this
                             orderby n.LevelGroup ascending, n.Week ascending
                             select n;

            foreach (BusinessTheme th in sortedList)
            {
                writer.WriteStartElement(ThemeElementName);
                th.WriteXml(writer);
                writer.WriteEndElement();
            }
        }

        #endregion

        #region Xml & Binary persistency

        public void SaveXml(String fileName)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(BusinessThemeCollection));

            using (TextWriter textWriter = new StreamWriter(fileName))
            {
                serializer.Serialize(textWriter, this);
            }
        }

        public static BusinessThemeCollection LoadXml(String fileName)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(BusinessThemeCollection));

            using (TextReader textReader = new StreamReader(fileName))
            {
                return (BusinessThemeCollection)serializer.Deserialize(textReader);
            }
        }

        /*public void MergeFromXml(String fileName, bool useTheirsOnConflict)
        {
            BusinessThemeCollection otherCollection = LoadXml(fileName);
            Merge(otherCollection, useTheirsOnConflict);
        }*/

        public void ReloadFromXml(String fileName)
        {
            Clear();

            BusinessThemeCollection otherCollection = LoadXml(fileName);
            Merge(otherCollection, true);
        }

        public void Save(String fileName)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            using (FileStream fileStream = new FileStream(fileName, FileMode.Create))
            {
                formatter.Serialize(fileStream, this);
            }
        }

        public static BusinessThemeCollection Load(String fileName)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            using (FileStream fileStream = new FileStream(fileName, FileMode.Open))
            {
                return (BusinessThemeCollection)formatter.Deserialize(fileStream);
            }
        }

        public void ReloadFrom(String fileName)
        {
            Clear();

            BusinessThemeCollection otherCollection = Load(fileName);
            Merge(otherCollection, true);
        }

        #endregion
    }

}
